﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security.Cryptography;
using System.IO;
using Toenda.Foundation.Serialization;

namespace Toenda.Foundation.Security.Cryptography {
	/// <summary>
	/// SymmCrypto is a wrapper of System.Security.Cryptography.SymmetricAlgorithm classes
	/// and simplifies the interface. It supports customized SymmetricAlgorithm as well.
	/// </summary>
	public class Crypto {
		/// <summary>
		/// Encrypts the string.
		/// </summary>
		/// <param name="clearText">The clear text.</param>
		/// <param name="Key">The key.</param>
		/// <param name="IV">The IV.</param>
		/// <returns></returns>
		private static byte[] EncryptString(byte[] clearText, byte[] Key, byte[] IV) {
			MemoryStream ms = new MemoryStream();
			Rijndael alg = Rijndael.Create();
			alg.Key = Key;
			alg.IV = IV;
			CryptoStream cs = new CryptoStream(ms, alg.CreateEncryptor(), CryptoStreamMode.Write);
			cs.Write(clearText, 0, clearText.Length);
			cs.Close();
			byte[] encryptedData = ms.ToArray();
			return encryptedData;
		}

		/// <summary>
		/// Encrypts the string.
		/// </summary>
		/// <param name="clearText">The clear text.</param>
		/// <param name="Password">The password.</param>
		/// <returns></returns>
		public static string EncryptString(string clearText, string Password) {
			byte[] clearBytes = System.Text.Encoding.Unicode.GetBytes(clearText);
			PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
			byte[] encryptedData = EncryptString(clearBytes, pdb.GetBytes(32), pdb.GetBytes(16));
			return Convert.ToBase64String(encryptedData);
		}

		/// <summary>
		/// Decrypts the string.
		/// </summary>
		/// <param name="cipherData">The cipher data.</param>
		/// <param name="Key">The key.</param>
		/// <param name="IV">The IV.</param>
		/// <returns></returns>
		private static byte[] DecryptString(byte[] cipherData, byte[] Key, byte[] IV) {
			MemoryStream ms = new MemoryStream();
			Rijndael alg = Rijndael.Create();
			alg.Key = Key;
			alg.IV = IV;
			CryptoStream cs = new CryptoStream(ms, alg.CreateDecryptor(), CryptoStreamMode.Write);
			cs.Write(cipherData, 0, cipherData.Length);
			cs.Close();
			byte[] decryptedData = ms.ToArray();
			return decryptedData;
		}

		/// <summary>
		/// Decrypts the string.
		/// </summary>
		/// <param name="cipherText">The cipher text.</param>
		/// <param name="Password">The password.</param>
		/// <returns></returns>
		public static string DecryptString(string cipherText, string Password) {
			byte[] cipherBytes = Convert.FromBase64String(cipherText);
			PasswordDeriveBytes pdb = new PasswordDeriveBytes(Password, new byte[] { 0x49, 0x76, 0x61, 0x6e, 0x20, 0x4d, 0x65, 0x64, 0x76, 0x65, 0x64, 0x65, 0x76 });
			byte[] decryptedData = DecryptString(cipherBytes, pdb.GetBytes(32), pdb.GetBytes(16));
			return System.Text.Encoding.Unicode.GetString(decryptedData);
		}

		public static string GenerateKey(int length) {
			if(length < 0) {
				length = 1;
			}

			if(length > 32) {
				length = 32;
			}

			return GenerateRandomString(length, true);
		}

		public static string GenerateIV() {
			return GenerateRandomString(16, false);
		}

		private static string GenerateRandomString(int length, bool withSpecialChars) {
			string ret = string.Empty;
			StringBuilder str = new StringBuilder();
			string content = "1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw";

			if(withSpecialChars) {
				content += "!()#?§*%$&=+-";
				// öäüÖÄÜß\"/
			}

			Random rnd = new Random();
			for(int i = 0; i < length; i++) {
				str.Append(content[rnd.Next(content.Length)]);
			}

			return str.ToString();
		}
	}
}
